import 'dart:async';
import 'dart:convert';
import 'package:flame/effects.dart';
import 'package:flame/flame.dart';
import 'package:flame/sprite.dart';
import 'package:flame/text.dart';
import 'package:flame/components.dart' hide Timer;
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:freedom/const/app_content.dart';
import 'package:freedom/const/game_const.dart';
import 'package:freedom/entity/mahjong/mahjong_room_record.dart';
import 'package:freedom/page/mahjong/bloc/room_bloc.dart';
import 'package:freedom/page/mahjong/bloc/room_bloc_state.dart';
import 'package:freedom/page/mahjong/bloc/user_state.dart';
import 'package:freedom/page/mahjong/common/message_content.dart';
import 'package:freedom/page/mahjong/bloc/room_info.dart';
import 'package:freedom/page/mahjong/event/room_inf_update.dart';
import 'package:freedom/page/mahjong/event/user_reflash_action.dart';
import 'package:freedom/page/mahjong/event/user_state_event.dart';
import 'package:freedom/page/mahjong/export_area/abstract_component.dart';
import 'package:freedom/page/mahjong/hand_area/abstract_card_area.dart';
import 'package:freedom/page/mahjong/keneck/mahjong_tips.dart';
import 'package:freedom/page/mahjong/keneck/room_rule_info_component.dart';
import 'package:freedom/page/mahjong/keneck/swap_card_component.dart';
import 'package:freedom/page/mahjong/message/action_message.dart';
import 'package:freedom/page/mahjong/util/user_location.dart';
import 'package:freedom/util/customer_dialog.dart';
import 'package:freedom/util/navigator_provider.dart';
import 'package:freedom/util/user_util.dart';
import 'package:provider/provider.dart';
import '../../entity/restful.dart';
import '../../entity/user_entity_info.dart';
import '../../util/api_router.dart';
import '../../util/http.dart';
import '../../util/socket_server.dart';
import 'common/disk_direction.dart';
import 'common/mahjong_context.dart';
import 'export_area/bottom.dart';
import 'export_area/left.dart';
import 'export_area/right.dart';
import 'export_area/top.dart';
import 'hand_area/bottom.dart';
import 'hand_area/left.dart';
import 'hand_area/right.dart';
import 'hand_area/top.dart';
import 'keneck/gaming_action_component.dart';
import 'keneck/mahjong_menu_component.dart';
import 'message/message_handler.dart';
import 'keneck/head_image_component.dart';
import 'keneck/residue_card_component.dart';

typedef LoadHomeBack = Function(RoomInfo roomInfo);

class MahjongIndexGame extends FlameGame {
  String roomCode;

  String? _multiCardActionCode;
  List<int>? _multiCardList;

  String? get multiCardActionCode => _multiCardActionCode;

  List<int>? get multiCardList => _multiCardList;

  void setMultiCard(String? multiCardActionCode, List<int>? multiCardList) {
    _multiCardActionCode = multiCardActionCode;
    _multiCardList = multiCardList;
  }

  /// 这几个区域
  AbstractExport? topExportArea;
  AbstractExport? leftExportArea;
  AbstractExport? rightExportArea;
  AbstractExport? bottomExportArea;

  // 换牌
  SwapCardAction? swapCardAction;

  // 事件
  final ActionMessageHandler mahjongAction = ActionMessageHandler();
  int playBackCurrentIndex = 1;
  int playBackMax = 1;
  bool playBackSuspend = false;
  bool switchUI = false;
  StreamController<double>? scheduleController;
  AbstractHand? topCardArea;
  AbstractHand? leftCardArea;
  AbstractHand? rightCardArea;
  AbstractHand? bottomCardArea;

  HeadImageComponent? topHead;
  HeadImageComponent? leftHead;
  HeadImageComponent? rightHead;
  HeadImageComponent? bottomHead;

  MahjongMenuComponent? mahjongMenuComponent;

  ResidueCard? _residueCard;
//房间规则 当前时间的提示
  RoomRuleInfo? _roomRuleInfo;
  // 回放定时器
  TimerComponent? _playBackTimerComp;

  // 东南西北
  DiskDirection? directionComponent;

  GamingActionComponent? gamingActionComponent;

  GameInfo? gameInfo;

  UserState? selectUser;

  String onHookStr = "挂机中...";

  void setSelectUser(UserState? selectUser) {
    this.selectUser = selectUser;
  }

  RoomBloc? roomBloc;
  final Object? record;
  MahjongIndexGame(
      {required this.roomCode, required this.roomBloc, this.record})
      : super();
  String src = 'mahjong/background/gaming_center.jpg';
  late ParallaxComponent parallaxComponent;
  final TextStyle _critDamageTextStyle = const TextStyle(
      fontSize: 50,
      color: Colors.white,
      fontFamily: 'Menlo',
      shadows: [
        Shadow(color: Colors.red, offset: Offset(1, 1), blurRadius: 1),
      ]);

  List<MahjongRoomRecord>? roomRecord;

  List<MahjongMessage>? beforeActions;
// 是否在执行上一个事件
  bool _isExecuting = false;
  @override
  Future<void> onLoad() async {
    /**
     * 初始化麻将的一些位置信息
     */
    MahjongContext().init(this);
    add(MahjongTips());
    if (record != null) {
      roomRecord = MahjongRoomRecord.build(record);
      playBackMax = roomRecord!.length;
      scheduleController = StreamController();
      MahjongContext().setPlayback = true;
      updateHome(roomRecord![0].roomInfo!);
      MahjongContext().showOverlay(SystemOverlay.playBackDialog);
      initPlayTimer();
    } else {
      MahjongContext().showOverlay(MahjongOverlay.huCardDialog);
      // 连接websocket
      connectServer(roomCode);
    }
  }

  void stopPlayTimer() {
    playBackSuspend = true;
    _playBackTimerComp?.removeFromParent();
    _playBackTimerComp = null;
  }

  void initPlayTimer() {
    _isExecuting = false;
    playBackSuspend = false;
    if (_playBackTimerComp != null) {
      return;
    }
    _playBackTimerComp = TimerComponent(
        period: 1,
        repeat: true,
        onTick: () async {
          if (!_isExecuting) {
            _isExecuting = true;
            try {
              if (playBackCurrentIndex > roomRecord!.length - 1 ||
                  playBackSuspend) {
                return;
              }
              if (beforeActions
                      ?.any((item) => item.type == ActionMessageHandler.DICE) ??
                  false) {
                await Future.delayed(const Duration(seconds: 3));
              }
              if (playBackSuspend) {
                return;
              }
              beforeActions = roomRecord![playBackCurrentIndex].actions;
              if (!playBackSuspend) {
                mahjongAction.doAction(beforeActions!);
              }
              scheduleController!.sink.add(playBackCurrentIndex.toDouble());
              playBackCurrentIndex++;
            } finally {
              _isExecuting = false;
            }
          }
        });
    add(_playBackTimerComp!);
  }

  void playIndex(int index) {
    if (playBackCurrentIndex - 1 == index) {
      return;
    }
    playBackCurrentIndex = index + 1;
    if (index > playBackMax - 1) {
      index = playBackMax - 1;
    }
    stopPlayTimer();
    roomRecord = MahjongRoomRecord.build(record);
    RoomInfo? roomInfo = roomRecord?[index].roomInfo;
    if (roomInfo == null) {
      return;
    }
    playBackSuspend = true;
    updateHome(roomInfo);
  }

  void addBBTH(String type, Direction direction) async {
    SpriteAnimationComponent spac = SpriteAnimationComponent();
    List<Sprite> sprites = [];
    for (int i = 1; i <= 12; i++) {
      sprites
          .add(Sprite(images.fromCache('mahjong/action/$type/${type}_$i.png')));
    }
    spac.animation =
        SpriteAnimation.spriteList(sprites, stepTime: 0.1, loop: false);
    switch (direction) {
      case Direction.TOP:
        spac.position = MahjongContext().topExportAreaPoint;
        break;
      case Direction.RIGHT:
        spac.position = MahjongContext().rightExportAreaPoint;
        break;
      case Direction.LEFT:
        spac.position = MahjongContext().leftExportAreaPoint;
        break;
      case Direction.BOTTOM:
        spac.position = MahjongContext().bottomExportAreaPoint;
        break;
      default:
        spac.position = MahjongContext().bottomExportAreaPoint;
    }
    spac.size = Vector2(300, 190);
    spac.anchor = Anchor.center;
    spac.removeOnFinish = true;
    add(spac);
  }

  void cleanUser() {
    MahjongContext().cleanCache();
    removeAll(Set.from(children));
    swapCardAction = null;
    leftCardArea = null;
    leftExportArea = null;
    leftHead = null;
    rightCardArea = null;
    rightExportArea = null;
    rightHead = null;
    topCardArea = null;
    topExportArea = null;
    topHead = null;
    bottomCardArea = null;
    bottomExportArea = null;
    bottomHead = null;
    directionComponent = null;
  }

  /// 这个函数更新了桌面所有信息
  /// 更新房间所有信息
  /// 如果为了方便可以直接调用此函数
  ///

  void updateHome(RoomInfo roomInfo) {
    gameInfo = roomInfo.gameInfo;
    // 初始化房间数据
    MahjongContext.initRoom(roomInfo);
    if (swapCardAction == null) {
      swapCardAction = SwapCardAction()..priority = 999;
      add(swapCardAction!);
    }
    if (_residueCard == null) {
      _residueCard = ResidueCard(roomInfo);
      add(_residueCard!);
    }

    List<UserState> allUser = roomInfo.gameInfo.allUser!;
    // 先加载本人  没有找到就默认加载第一条
    UserState currentUser = allUser.singleWhere(
      (element) => element.userId == UserUtil.getUserInfo()?.sysUser.id,
      orElse: () => allUser[0],
    );
    // 初始化当前用户信息
    addUser(currentUser, roomInfo, Direction.BOTTOM);
    // 显示战绩
    MahjongContext().showGameBalanceWidget(roomInfo);
    /*  MahjongContext().showGameOverWidget(roomInfo); */
    MahjongContext().showOverlay(MahjongOverlay.swapCardInfo);
    MahjongContext().cleanHuCardTips();
    for (var element in allUser) {
      if (element.userId == currentUser.userId) {
        continue;
      }
      Direction userPoint = LocationUtil.buildLocationUtil(
          roomInfo.rule!.userCount, currentUser.seq, element.seq);
      addUser(element, roomInfo, userPoint);
    }
    //判断玩家是否已经退出，退出了移出玩家的数据
    MahjongContext.diskAllUser.forEach((key, value) {
      if (allUser.any((element) => element.userId == key)) {
        return;
      }
      switch (value.direction) {
        case Direction.LEFT:
          leftHead?.removeFromParent();
          leftCardArea?.removeFromParent();
          leftExportArea?.removeFromParent();
          leftCardArea = null;
          leftExportArea = null;
          leftHead = null;
          break;
        case Direction.RIGHT:
          rightHead?.removeFromParent();
          rightCardArea?.removeFromParent();
          rightExportArea?.removeFromParent();
          rightCardArea = null;
          rightExportArea = null;
          rightHead = null;
          break;
        case Direction.TOP:
          topHead?.removeFromParent();
          topCardArea?.removeFromParent();
          topExportArea?.removeFromParent();
          topCardArea = null;
          topExportArea = null;
          topHead = null;
          break;
        default:
          break;
      }
    });

    // 菜单为空的话添加菜单
    if (mahjongMenuComponent == null && record == null) {
      mahjongMenuComponent = MahjongMenuComponent();
      add(FlameBlocProvider<RoomBloc, RoomBlocState>(
        create: () => roomBloc!,
        children: [mahjongMenuComponent!],
      ));
    }
    //因为这里需要决定庄家  所以必须要添加庄家数据才能添加东南西北风
    if (directionComponent == null) {
      directionComponent = DiskDirection(current: Direction.BOTTOM);
      add(directionComponent!);
    }

    if (_roomRuleInfo == null) {
      _roomRuleInfo = RoomRuleInfo();
      add(_roomRuleInfo!);
    }
    MahjongContext.postRoomEvent<RoomInfoUpdateEvent>(
        RoomInfoUpdateEvent(roomInfo));
  }

  void readHome(LoadHomeBack loadHomeBack) async {
    await Http.getInstance().request<Restful>(ApiRouter.readRoom,
        type: RequestEnum.GET,
        showLoading: false,
        showErrorMsg: true,
        loadingMsg: "读取中...",
        data: {"roomCode": roomCode}, success: (data) {
      RoomInfo roomInfo = RoomInfo.formJSON(data.body);
      if (roomInfo.code == null) {
        return;
      }
      loadHomeBack(roomInfo);
    }, error: (data) {
      MahjongContext().gameOverAndToIndex();
    });
  }

  void addUser(
      UserState userState, RoomInfo roomInfo, Direction direction) async {
    AbstractExport? currentExportArea;
    AbstractHand? currentCardArea;
    HeadImageComponent? currentHead;
    MahjongContext.setUserState(direction, userState);
    switch (direction) {
      case Direction.TOP:
        if (topExportArea == null) {
          topExportArea = TopExportArea();
          add(topExportArea!);
        }
        if (topCardArea == null) {
          topCardArea = TopCardArea();
          add(topCardArea!);
        }

        if (topHead == null) {
          topHead = HeadImageComponent(direction: direction);
          add(topHead!);
        }
        currentExportArea = topExportArea;
        currentCardArea = topCardArea;
        currentHead = topHead;
        break;
      case Direction.BOTTOM:
        if (bottomExportArea == null) {
          bottomExportArea = BottomExportArea();
          add(bottomExportArea!);
        }
        if (bottomCardArea == null) {
          bottomCardArea = BottomCardArea();
          add(bottomCardArea!);
        }
        if (gamingActionComponent == null && record == null) {
          gamingActionComponent = GamingActionComponent();
          add(gamingActionComponent!);
        }
        if (bottomHead == null) {
          bottomHead = HeadImageComponent(direction: direction);
          add(bottomHead!);
        }
        currentExportArea = bottomExportArea;
        currentCardArea = bottomCardArea;
        currentHead = bottomHead;
        MahjongContext()
            .updateSwapCard(userState.swapCardIn, userState.swapCardOut);
        break;
      case Direction.RIGHT:
        if (rightExportArea == null) {
          rightExportArea = RightExportArea();
          add(rightExportArea!);
        }

        if (rightCardArea == null) {
          rightCardArea = RightCardArea();
          add(rightCardArea!);
        }

        if (rightHead == null) {
          rightHead = HeadImageComponent(
            direction: direction,
          );
          add(rightHead!);
        }
        currentExportArea = rightExportArea;
        currentCardArea = rightCardArea;
        currentHead = rightHead;
        break;
      case Direction.LEFT:
        if (leftExportArea == null) {
          leftExportArea = LeftExportArea();
          add(leftExportArea!);
        }

        if (leftCardArea == null) {
          leftCardArea = LeftCardArea();
          add(leftCardArea!);
        }

        if (leftHead == null) {
          leftHead = HeadImageComponent(direction: direction);
          add(leftHead!);
        }
        currentExportArea = leftExportArea;
        currentCardArea = leftCardArea;
        currentHead = leftHead;
        break;
      default:
    }

    currentExportArea!.reflashUI(userState.getExportCards());

    /**
      * 处理换牌 试图
      */
    if (roomInfo.checkExistsRule("HSZ")) {
      if (MahjongContext().getPlayback) {
        if (userState.swapCardIn == null && userState.swapCardOut != null) {
          swapCardAction?.swapCard(direction,
              swapCard: MahjongContext().getPlayback
                  ? userState.swapCardOut
                      ?.split(",")
                      .map((e) => int.parse(e))
                      .toList()
                  : null);
        } else {
          swapCardAction?.swapCard(direction, insert: false);
        }
      } else {
        if (userState.swapOutStatus && !userState.swapInStatus) {
          swapCardAction?.swapCard(direction,
              swapCard: MahjongContext().getPlayback
                  ? userState.swapCardOut
                      ?.split(",")
                      .map((e) => int.parse(e))
                      .toList()
                  : null);
        } else {
          swapCardAction?.swapCard(direction, insert: false);
        }
      }
    }

    MahjongContext.initUserEvent(userState,
        direction: direction,
        hand: currentCardArea!,
        export: currentExportArea,
        head: currentHead!);
    //执行用户事件更新
    MahjongContext.postUserEvent<UserReflashActionEvent>(
        userState.userId, UserReflashActionEvent(userState.getActions()));
    //执行用户状态更新
    MahjongContext.postUserEvent<UserStateUpdateEvent>(
        userState.userId, UserStateUpdateEvent(userState));
  }

  void exportCardToDisk() {}

  @override
  @mustCallSuper
  void onGameResize(Vector2 size) {
    MahjongContext().initScreen(size.x, size.y);
    super.onGameResize(size);
  }

  //http://127.0.0.1:8081/Xpocker/test?message=test
  void connectServer(String roomCode) {
    var loginUser = Provider.of<UserEntityInfo>(
        NavigatorProvider.navigatorContext,
        listen: false);
    String url =
        "${ApiRouter.getSocketServer()}?&roomCode=${roomCode.toString()}";
    SocketServer().connect(url, loginUser.tokenAuthority!, conSuccess: (conn) {
      readHome(updateHome);
    }, onMessage: (data) async {
      var serverMessage = MessageContent.fromJson(json.decode(data));
      // 当本地的版本 与服务器版本差异过大的时候  更新房间
      if (serverMessage.synVersion > 0 &&
          MahjongContext.syncVersion - serverMessage.synVersion > 1) {
        readHome(updateHome);
      } else if (serverMessage.synVersion > 0) {
        // 每一次收到消息说明房间有更新
        MahjongContext.syncVersion = serverMessage.synVersion;
      }
      MessageHandler().doMessage(serverMessage);
    }, serverClose: () {
      if (MahjongContext.roomInfo.status != MahjongRoomStatus.over) {
        MahjongContext().gameOverAndToIndex();
        CustomerDialog.showText("房间被关闭");
      }
    });
  }

  @override
  @mustCallSuper
  void render(Canvas canvas) {
    //先添加背景
    var mjBgImage = images.fromCache(src);
    Rect rect = Rect.fromLTWH(0, 0, size.x, size.y);
    Sprite(mjBgImage).renderRect(canvas, rect);
    //再添加 component
    super.render(canvas);
    if ((MahjongContext.currentUser?.onHook ?? false) && record == null) {
      // 绘制第三行文本
      final thirdLinePainter = TextPainter(
        text: TextSpan(
            text: onHookStr,
            style: TextStyle(
                color: const Color.fromARGB(255, 255, 255, 255),
                fontSize: 20.h,
                height: 1.0,
                fontFamily: AppContent.fontHyqmd)),
        textDirection: TextDirection.ltr,
        ellipsis: "...",
        maxLines: 3, // 设置最大行数
      );
      thirdLinePainter.layout();

      // 创建渐变
      var gradient = const LinearGradient(
        colors: [Colors.blue, Color.fromARGB(71, 0, 0, 0)], // 渐变的颜色
        begin: Alignment.centerLeft, // 渐变的起点
        end: Alignment.centerRight, // 渐变的终点
      );
      // 创建渐变画笔
      final gradientPaint = Paint()
        ..shader = gradient.createShader(Rect.fromLTWH(
            0, 0, thirdLinePainter.width, thirdLinePainter.height));

      canvas.drawRect(
          Rect.fromLTWH(
              size.x / 2 - thirdLinePainter.width,
              MahjongContext().bottomExportAreaPoint.y,
              thirdLinePainter.width * 2,
              thirdLinePainter.height),
          gradientPaint);
      thirdLinePainter.paint(
          canvas,
          Offset((size.x - thirdLinePainter.width) / 2,
              MahjongContext().bottomExportAreaPoint.y));
    }

    var textPrint = Paint()
      ..shader = const LinearGradient(
        colors: [Color(0x00073523), Color(0x00A41e13)], // 渐变色列表
        begin: Alignment.bottomCenter, // 渐变起点
        end: Alignment.topCenter, // 渐变终点
      ).createShader(Rect.fromLTWH(0, size.y / 4, size.x, size.y));
    canvas.drawRect(rect, textPrint);

    var lTextStyle = const TextStyle(
        color: Colors.black12,
        fontSize: 12,
        fontWeight: FontWeight.w900,
        shadows: [
          Shadow(color: Colors.black12, offset: Offset(2, 2), blurRadius: 2),
        ]);
    var lTextSpan = TextSpan(text: "逍  遥  棋  牌", style: lTextStyle);
    var lTextPainter = TextPainter(
      text: lTextSpan,
      textDirection: TextDirection.rtl,
    );
    lTextPainter.layout();
    lTextPainter.paint(
        canvas,
        Offset(
            (size.x - lTextPainter.width) / 2,
            MahjongContext().bottomHandCardAreaPoint.y -
                lTextPainter.height * 2));
  }

  Future<void> addGifMessage(Direction? direction, EmojiInfo? emojiInfo) async {
    if (direction == null || emojiInfo == null) {
      return;
    }
    int cycleIndex = emojiInfo.playCount;
    int imageCount = emojiInfo.imageCount;
    SpriteSheet spriteSheet = SpriteSheet.fromColumnsAndRows(
      image: Flame.images.fromCache(emojiInfo.pathName),
      columns: emojiInfo.column,
      rows: emojiInfo.row,
    );
    SpriteAnimationComponent spac = SpriteAnimationComponent();
    switch (direction) {
      case Direction.LEFT:
        spac.position = Vector2(
            MahjongContext().lImagePoint.x + MahjongContext().headImageHeight,
            MahjongContext().lImagePoint.y);
        spac.anchor = Anchor.topLeft;
      case Direction.RIGHT:
        spac.position = Vector2(
            MahjongContext().rImagePoint.x, MahjongContext().rImagePoint.y);
        spac.anchor = Anchor.topRight;
      case Direction.BOTTOM:
        spac.position = Vector2(
            MahjongContext().bImagePoint.x +
                MahjongContext().headImageHeight * 2,
            MahjongContext().bImagePoint.y + MahjongContext().headImageHeight);
        spac.anchor = Anchor.bottomLeft;
      case Direction.TOP:
        spac.position = Vector2(
            MahjongContext().tImagePoint.x +
                MahjongContext().headImageHeight * 2,
            MahjongContext().tImagePoint.y);
        spac.anchor = Anchor.topLeft;
    }
    List<Sprite> sprites = [];
    for (int i = 0; i <= emojiInfo.imageCount - 1; i++) {
      sprites.add(spriteSheet.getSprite(0, i));
    }
    spac.animation = SpriteAnimation.spriteList(sprites,
        stepTime: emojiInfo.sleep, loop: true);
    spac.size = Vector2.all(MahjongContext().headImageHeight * 2);
    spac.priority = 999;
    spac.animationTicker?.onFrame = (index) {
      if (index == imageCount - 1) {
        cycleIndex--;
      }
      if (cycleIndex == 0) {
        remove(spac);
      }
    };
    add(spac);
  }

  Future<void> _addCritDamage(String damage) async {
    TextComponent damageText =
        TextComponent(textRenderer: TextPaint(style: _critDamageTextStyle));
    damageText.text = damage.toString();
    damageText.position = size / 2;
    add(damageText);
    damageText.add(MoveToEffect(
      Vector2.all(0),
      EffectController(
        duration: 2,
      ),
    ));
    //await Future.delayed(const Duration(seconds: 1));
    //damageText.removeFromParent();
  }

  double timer = 0;

  @override
  void update(double dt) {
    super.update(dt);
    timer += dt;
    if (timer >= 0.5) {
      onHookStr += '.';
      if (onHookStr.length > 6) {
        onHookStr = '挂机中';
      }
      timer -= 1; // 重置计时器
    }
  }
}
